package org.shaka.db.sqlbuilder.component;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.shaka.db.sqlbuilder.sql.SqlTable;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public abstract class Table extends AbstractNamePartial {
    private String schema;
    private Map<String, JoinInfo> joins;

    public Table(String schema, String name) {
        super(name);
        this.schema = schema;
        joins = Maps.newHashMap();
    }

    public JoinInfo getJoin(Table table) {
        return joins.get(table.name());
    }

    public SqlTable as(String as) {
        return new SqlTable(this, as);
    }

    public void addJoinLinkInfo(Table joinTable, PairColumns... columns) {
        addJoinLinkInfo(true, joinTable, columns);
    }

    private void addJoinLinkInfo(boolean eachother, Table joinTable, PairColumns... columns) {
        JoinInfo join = new JoinInfo(joinTable, columns);
        joins.put(joinTable.name(), join);
        if (eachother) {
            joinTable.addJoinLinkInfo(false, this,
                    Lists.transform(join.getPairColumns(), new Function<PairColumns, PairColumns>() {
                        @Override
                        public PairColumns apply(PairColumns input) {
                            return new PairColumns(input.getJoinColumn(), input.getSelfColumn());
                        }
                    }).toArray(new PairColumns[columns.length]));
        }
    }

    @Override
    public String name() {
        if (StringUtils.isNotBlank(schema))
            return String.format("%s.%s", schema, super.name());
        return super.name();
    }

    public abstract List<Column> primaryKeys();

    public abstract List<Column> columns();

    public static class JoinInfo {
        private final Table joinTable;
        private final List<PairColumns> pairColumns;

        public JoinInfo(Table joinTable, PairColumns[] pairColumns) {
            this.joinTable = joinTable;
            this.pairColumns = Arrays.asList(pairColumns);
        }

        public Table getJoinTable() {
            return joinTable;
        }

        public List<PairColumns> getPairColumns() {
            return pairColumns;
        }
    }

    public static class PairColumns {
        private final Column selfColumn;
        private final Column joinColumn;

        public PairColumns(Column selfColumn, Column joinColumn) {
            this.selfColumn = selfColumn;
            this.joinColumn = joinColumn;
        }

        public static PairColumns valueOf(Column selfColumn, Column joinColumn) {
            return new PairColumns(selfColumn, joinColumn);
        }

        public Column getSelfColumn() {
            return selfColumn;
        }

        public Column getJoinColumn() {
            return joinColumn;
        }
    }
}
